User experience is very important for your plugin. With WooCommerce Settings Pages, this experience might become worse with a lot of fields on one page. In this tutorial, you will learn how to create additional WooCommerce settings pages for your Payment Gateway.
I want this tutorial to be really focused, so I won’t go into developing a custom payment gateway. I’ll just use the existing PayPal gateway in WooCommerce and extend it with new fields and additional WooCommerce settings pages.
We will actually create two WooCommerce settings pages. The first one will be used for showing other fields and saving them. The second one will be used to show the orders done by our Payment Gateway.
Create an empty php
file in the plugins
folder, name it however you want and activate it through the dashboard.
Replacing the default PayPal Payment Gateway
Let’s replace it with our own. To do that, we will need to go through the registered payment gateways, remove the existing one and add our own.
<?php | |
add_action( 'plugins_loaded', function(){ | |
if( class_exists( 'WC_Gateway_Paypal' ) ) { | |
class WC_Custom_PayPal extends WC_Gateway_Paypal { | |
// Other Code will go here. | |
} | |
add_action( 'woocommerce_payment_gateways', 'wc_change_paypal' ); | |
/** | |
* Removing the existing PayPal Gateway and adding our own. | |
* | |
* @param array $gateways All Registered Gateways | |
* @return array | |
*/ | |
function wc_change_paypal( $gateways ) { | |
for( $i = 0; $i < count( $gateways ); $i++ ) { | |
if( 'WC_Gateway_Paypal' === $gateways[ $i ] ) { | |
unset( $gateways[ $i ] ); | |
} | |
} | |
$gateways[] = 'WC_Custom_PayPal'; | |
return $gateways; | |
} | |
} |
We are using the filter woocommerce_payment_gateways
to hook into all the registered gateways and remove the existing one. We are also making our own WC_Custom_PayPal
as an extended class from WC_Gateway_Paypal
. By doing this, we are retaining everything from the default class.
You could load the settings page now and it would work like nothing has happened.
Adding Buttons for Other WooCommerce Settings Pages
To add buttons that will link to other pages, we will create a custom field.
<?php | |
class WC_Custom_PayPal extends WC_Gateway_Paypal { | |
/** | |
* Setting a screen button in fields | |
* @return void | |
*/ | |
public function init_form_fields() { | |
parent::init_form_fields(); | |
$this->form_fields = array_merge( $this->form_fields, array( | |
'screen_button' => array( | |
'id' => 'screen_button', | |
'type' => 'screen_button', | |
'title' => __( 'Other Settings', 'custom_paypal' ), | |
) | |
)); | |
} | |
} |
We are now redefining the method init_form_fields
and inside that method we are calling the parent one. That will register all the default fields and then we will add our own field by merging the previous ones with our new array.
For now, if you load the PayPal settings page, you will just see a text input field. That is because we have not yet defined the HTML that we want for it. Let’s do that now.
By creating a method generate_screen_button_html
we are defining the method for the custom field type screen_button
. WooCommerce loads fields’ HTML by checking the type against existing methods. If there is no such method, it will load, by default, the HTML for the text input.
If you load the PayPal Settings Page, you should see those two buttons.
If you click on any of those buttons, you will get a new query string screen
in the URL. For now, nothing will happen. You will still see the same default page.
Changing the WooCommerce Settings Page Output
To display different fields or other content, we need to redefine the method that is outputting the default settings fields. That method is admin_options
. If you redefine it in our class, the output will change.
<?php | |
class WC_Custom_PayPal extends WC_Gateway_Paypal { | |
/** | |
* Redefining the display of options. | |
* If we are on a second screen, we will show the other fields. | |
* If we are not on the second screen, show the original fields. | |
*/ | |
public function admin_options() { | |
if( ! isset( $_GET['screen'] ) || '' === $_GET['screen'] ) { | |
parent::admin_options(); | |
} else { | |
if( 'orders' === $_GET['screen'] ) { | |
echo '<h2><a href="' . admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=paypal' ) . '">' . $this->method_title . '</a> > ' . __( 'Orders done with PayPal', 'custom_paypal' ) . '</h2>'; | |
$hide_save_button = true; // Remove the submit button. | |
$orders = wc_get_orders(array( | |
'limit' => '-1', | |
'payment_method' => $this->id, | |
)); | |
if( $orders ) { | |
// Code for Orders will go here | |
} | |
} else { | |
echo '<h2><a href="' . admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=paypal' ) . '">' . $this->method_title . '</a> > ' . __( 'Other Settings', 'custom_paypal' ) . '</h2>'; | |
echo '<table class="form-table">'; | |
WC_Admin_Settings::output_fields( $this->other_form_fields() ); | |
echo '</table>'; | |
} | |
} | |
} | |
} |
We check for the new query string first. If the query string screen
is not present, we will call the parent admin_options
so that it performs the default functionality and shows the default fields.
If there is a screen, we are then checking the content of it. If we are to display the orders, then we will process some custom HTML. By setting $hide_save_button
to true
, we will hide the submit button on that screen. We are retrieving the orders that are done only by this method. The custom HTML will be done at the end of this article.
If we are on the other screen, we will show the other settings by calling WC_Admin_Settings::output_fields()
. Since we are using a custom method other_form_fields()
to retrieve those fields, let’s create it.
Other Fields
To define other fields, we will create an array in the same way as the default fields.
<?php | |
class WC_Custom_PayPal extends WC_Gateway_Paypal { | |
/** | |
* Form Fields for Other Screen | |
*/ | |
public function other_form_fields() { | |
return array( | |
'convert' => array( | |
'type' => 'checkbox', | |
'id' => 'convert', | |
'title' => __( 'Convert?', 'custom_paypal' ), | |
'description' => __( 'Convert other currencies?', 'custom_paypal' ), | |
), | |
); | |
} | |
} |
This is just a simple showcase on how you could define other fields, so you can learn how to do it. This particular setting won’t do a thing when processing payments. You can define whatever fields you need here.
Saving the Fields on the additional WooCommerce Settings Pages
If we don’t do anything now, WooCommerce, by default, will process the default fields because the method process_admin_options
is set to process only the default fields.
If we are on the other page and try to save it, all the default settings will be RESET. They will reset because no data will be passed to the $_POST
variable so each default form field will appear as empty.
We need to process only the other fields on our additional WooCommerce Settings Pages. So how to do that? By redefining the method process_admin_options
.
<?php | |
class WC_Custom_PayPal extends WC_Gateway_Paypal { | |
/** | |
* Redefining how options are saved for this gateway. | |
* If we are on a second screen, we will save the other fields. | |
* If we are not on the second screen, save the original fields. | |
*/ | |
public function process_admin_options() { | |
if( isset( $_GET['screen'] ) && '' !== $_GET['screen'] ) { | |
WC_Admin_Settings::save_fields( $this->other_form_fields() ); | |
} else { | |
parent::process_admin_options(); | |
} | |
} | |
} |
In this example, we are checking if we are on a screen. If we are not, then we will process the default fields by calling the parent definition of our method. If we are on a screen, we will process the other form fields by calling WC_Admin_Settings::save_fields()
.
To be sure that your other fields are not reset, you can go a step further and define the specific screen to be used for saving the fields.
Showing Orders Paid WooCommerce Settings Page
We will now go through all the orders that we got from the previous query inside the method admin_options
.
For showing this orders, I’ve copied the template used in myacount/orders.php
. I have stripped it down to just a table and I have removed the action
column from that table.
<?php | |
//... Goes in public function admin_options() { | |
if( $orders ) { | |
?> | |
<table class="form-table"> | |
<thead> | |
<tr> | |
<?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : | |
if( 'order-actions' === $column_id ) { | |
continue; | |
} | |
?> | |
<td><strong><?php echo esc_html( $column_name ); ?></strong></td> | |
<?php endforeach; ?> | |
</tr> | |
</thead> | |
<tbody> | |
<?php foreach ( $orders as $customer_order ) : | |
$order = wc_get_order( $customer_order ); | |
$item_count = $order->get_item_count(); | |
?> | |
<tr class="woocommerce-orders-table__row woocommerce-orders-table__row--status-<?php echo esc_attr( $order->get_status() ); ?> order"> | |
<?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?> | |
<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-<?php echo esc_attr( $column_id ); ?>" data-title="<?php echo esc_attr( $column_name ); ?>"> | |
<?php if ( 'order-number' === $column_id ) : ?> | |
<a href="<?php echo esc_url( $order->get_view_order_url() ); ?>"> | |
<?php echo _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number(); ?> | |
</a> | |
<?php elseif ( 'order-date' === $column_id ) : ?> | |
<time datetime="<?php echo esc_attr( $order->get_date_created()->date( 'c' ) ); ?>"><?php echo esc_html( wc_format_datetime( $order->get_date_created() ) ); ?></time> | |
<?php elseif ( 'order-status' === $column_id ) : ?> | |
<?php echo esc_html( wc_get_order_status_name( $order->get_status() ) ); ?> | |
<?php elseif ( 'order-total' === $column_id ) : ?> | |
<?php | |
/* translators: 1: formatted order total 2: total order items */ | |
printf( _n( '%1$s for %2$s item', '%1$s for %2$s items', $item_count, 'woocommerce' ), $order->get_formatted_order_total(), $item_count ); | |
?> | |
<?php endif; ?> | |
</td> | |
<?php endforeach; ?> | |
</tr> | |
<?php endforeach; ?> | |
</tbody> | |
</table> | |
<?php } else { | |
echo '<p>' . __( 'No orders done yet with this gateway', 'custom_paypal' ) . '</p>'; | |
} |
You can use this to display even more data if you want.
Plugin Files
If you want the complete code, you can download it here and then do whatever you want with it:)
This part is available only to the members. If you want to become a member and support my work go to this link and subscribe: Become a Member
Conclusion
By using OOP and WooCommerce hooks, we can completely change how a default Payment Gateway operates. This can be also done for other settings pages.
Have you ever had to change a Gateway functionality? Would additional pages enhance the UX of your WooCommerce plugin? Tell us what you think in the comments below.
Become a Sponsor
Share this: